package com.hevs.samplewebapp.client.screens;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import javax.validation.ValidationException;

import com.google.gwt.i18n.client.DateTimeFormat;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.logical.shared.CloseEvent;
import com.google.gwt.event.logical.shared.CloseHandler;
import com.google.gwt.json.client.JSONArray;
import com.google.gwt.json.client.JSONNumber;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONString;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.datepicker.client.DatePicker;

import com.hevs.samplewebapp.client.Patient;
import com.hevs.samplewebapp.client.bean.Category;
import com.hevs.samplewebapp.client.bean.CompositeWRefresh;
import com.hevs.samplewebapp.client.bean.FieldVerifier;
import com.hevs.samplewebapp.client.bean.MedicalDataItem;
import com.hevs.samplewebapp.client.bean.Type;
import com.hevs.samplewebapp.client.dialogbox.ConfirmDialogBox;
import com.hevs.samplewebapp.client.services.MedicalDataServiceImpl;

/**
 * Widget to add new medical data
 * <p>It provide methods for adding new data.</p>
 *  
 * @author Lionel Rey, Thomas Rouvinez, Peter Schnidrig
 * @date 12.03.2012
 */

public class AddMedicalDataWidget extends CompositeWRefresh{
        // ------------------------------------------------------
        // Variables.
        // ------------------------------------------------------
        
        /**
         * Vertical panel that is the main panel 
         */
        private VerticalPanel mainPanel = new VerticalPanel();
        
        /**
         * Horizontal panel it contains the field for new data
         */
        private HorizontalPanel dataPanel = new HorizontalPanel();
        
        /**
         * Horizontal panel it contains the buttons to add the data and reset fields
         */
        private HorizontalPanel buttonsPanel = new HorizontalPanel();
        
        /**
         * Vertical panel it contain fields for the data how need a category 
         */
        private VerticalPanel rightSide = new VerticalPanel();

        /**
         * Flextable with the fields without category
         */
        private FlexTable normalValues = new FlexTable();
        
        /**
         * Flextable with the fields with category
         */
        private FlexTable categoryValues = new FlexTable();
        
        /**
         * Flextable with the datePicker
         */
        private FlexTable datePickerGUI = new FlexTable();
        
        /**
         * List of medical data to add
         */
        private List<MedicalDataItem> medicalData = new ArrayList<MedicalDataItem>();

        /**
         * Textbox for the value of blood sugar
         */
        private TextBox tbBloodSugar = new TextBox();
        
        /**
         * Textbox for the value of quantity of insuline
         */
        private TextBox tbQuantityOfInsulin = new TextBox();
        
        /**
         * ListBox with the different type of insuline
         */
        private ListBox lbTypeOfInsuline = new ListBox();
        
        /**
         * Textbox for the value of the weight
         */
        private TextBox tbWeight = new TextBox();
        
        /**
         * Textbox with the value of the diastolic
         */
        private TextBox tbBloodPresureDiastolic = new TextBox();
        
        /**
         * Textbox with the value of the systolic
         */
        private TextBox tbBloodPresureSystolic = new TextBox();
        
        /**
         * Textbox with the value of the pulse rate
         */
        private TextBox tbPulseRate = new TextBox();
        
        /**
         * Button to add the data
         */
        private Button btAddData = new Button("Add data");
        
        /**
         * Button to reset all the fields
         */
        private Button btResetData = new Button("Reset data");
        
        /**
         * Date picker to choose the date of measures
         */
        private DatePicker dpDateOfMeasure = new DatePicker();
        
        /**
         * Listbox with the different category of time of the day
         */
        private ListBox lbCategoryOfMeasure = new ListBox();
        
        /**
         * Service implementation for the patient
         * @see MedicalDataServiceImpl
         */
        private MedicalDataServiceImpl medicalDataService = new MedicalDataServiceImpl();

        /**
         * widget to manage medical data
         * @see ManageMedicalDataWidget
         */
        private ManageMedicalDataWidget manageDataWidget = new ManageMedicalDataWidget();

        // ----------------------------------------------------------------------------
        // Constructor.
        // ----------------------------------------------------------------------------

        /**
         * Constructor of the widget, call initialize the widget method.
         */
        public AddMedicalDataWidget(){
                // Set date picker to current date.
                buildAddDataFlexTable();

                // Widget initialization.
                initWidget(mainPanel);
        }

        // ----------------------------------------------------------------------------
        // Methods.
        // ----------------------------------------------------------------------------

        /**
         * Method to build the flexTable with all the GUIelements inside.
         */
        private void buildAddDataFlexTable() {
                // Temp array for insuline types
                ArrayList<Integer> insulineTypes = new ArrayList<Integer>();

                dpDateOfMeasure.setValue(new Date(), true);

                // Fill the ListBoxes with defined Types and Categories
                for(int i=0;i<Category.getSize();i++)
                {
                        Category temp=Category.get(i);
                        lbCategoryOfMeasure.addItem(temp.getName(),String.valueOf(temp.getLevel()));
                }
                for(int i=0,j=Type.INSULINE_INDEX;i<Type.getSize();i++,j++)
                        if(Type.getIndex(j)!=-1)
                                insulineTypes.add(j);
                for(int i=0;i<insulineTypes.size();i++)
                {
                        Type temp=Type.getFromVal(insulineTypes.get(i));
                        lbTypeOfInsuline.addItem(temp.getName(),String.valueOf(temp.getLevel()));
                }

                // ----------------------------------------------------------------------------
                // Build the layout.
                // ----------------------------------------------------------------------------
                
                // Normal values form.
                normalValues.setText( 0, 0, "Diastolic Blood Presure : ");
                normalValues.setWidget(1, 0, tbBloodPresureDiastolic);
                normalValues.setText(2, 0, "Systolic Blood Presure : ");
                normalValues.setWidget(3, 0, tbBloodPresureSystolic);
                normalValues.setText(4, 0, "Weight (kg) : ");
                normalValues.setWidget(5, 0, tbWeight);
                normalValues.setText(6, 0, "Pulse rate (BPM)");
                normalValues.setWidget(7, 0, tbPulseRate);
                
                dataPanel.add(normalValues);
                
                // Category-related values form.
                categoryValues.setText(0, 0, "Category : ");
                categoryValues.setWidget(1, 0, lbCategoryOfMeasure);
                lbCategoryOfMeasure.setWidth("150px");
                categoryValues.setText(2, 0, "Blood Sugar (mmol/L) : ");
                categoryValues.setWidget(3, 0, tbBloodSugar);
                categoryValues.setText(4, 0, "Type of insulin : ");
                lbTypeOfInsuline.setWidth("150px");
                categoryValues.setWidget(5, 0, lbTypeOfInsuline);
                categoryValues.setText(6, 0, "Quantity of taken insulin : " );
                categoryValues.setWidget(7, 0, tbQuantityOfInsulin);
                
                rightSide.add(categoryValues);
                
                // Set space between flexTables.
                Label spacePanel = new Label(" ");
                Label spacePanel2 = new Label(" ");
                Label space = new Label(" ");
                Label spaceButtons = new Label(" ");
                spacePanel.setWidth("70px");
                spacePanel2.setWidth("70px");
                spaceButtons.setWidth("70px");
                space.setWidth("135px");
                
                dataPanel.add(spacePanel);
                dataPanel.add(rightSide);
                
                // Date picker.
                datePickerGUI.setText(0, 0, "Date of measure : ");
                datePickerGUI.setWidget(1, 0, dpDateOfMeasure);
                
                // Final layout.
                dataPanel.add(spacePanel2);
                dataPanel.add(datePickerGUI);
                mainPanel.add(dataPanel);       
                buttonsPanel.add(btAddData);
                buttonsPanel.add(space);
                buttonsPanel.add(btResetData);
                
                mainPanel.add(spaceButtons);
                mainPanel.add(buttonsPanel);

                // Add listeners for the add data and reset data buttons.
                btAddData.addClickHandler(new AddDataHandler());
                btResetData.addClickHandler(new ResetDataHandler());
        }

        /**
         * Method to reset the Fields.
         */
        public void resetFields(){
                // Clear all textBoxes.
                tbBloodPresureDiastolic.setText("");
                tbBloodPresureSystolic.setText("");
                tbBloodSugar.setText("");
                tbPulseRate.setText("");
                tbQuantityOfInsulin.setText("");
                tbWeight.setText("");

                // Reset date picker to current date.
                dpDateOfMeasure.setValue(new Date(), true);
        }

        /**
         * Method to create a list of medicalDataItem
         */
        public void createMedicalData(){
                medicalData.clear();
                // create dateFormater to show only dd.MM.yyyy
                DateTimeFormat dateFormat = DateTimeFormat.getFormat("dd.MM.yyyy");
                String date = dateFormat.format(dpDateOfMeasure.getValue()).toString();
                if(tbBloodSugar.getValue()!=""){
                        medicalData.add(new MedicalDataItem(Integer.parseInt(tbBloodSugar.getText()), date, 
                                        lbCategoryOfMeasure.getSelectedIndex(), 0));
                }
                if(tbQuantityOfInsulin.getValue()!=""){
                        if(lbTypeOfInsuline.getSelectedIndex() == 0){
                                medicalData.add(new MedicalDataItem(Integer.parseInt(tbQuantityOfInsulin.getText()), date, 
                                                lbCategoryOfMeasure.getSelectedIndex(), lbTypeOfInsuline.getSelectedIndex()+11));
                        }
                        else{
                                medicalData.add(new MedicalDataItem(Integer.parseInt(tbQuantityOfInsulin.getText()), date, 
                                                lbCategoryOfMeasure.getSelectedIndex(), lbTypeOfInsuline.getSelectedIndex()+12));
                        }
                }
                if(tbWeight.getValue()!=""){
                        medicalData.add(new MedicalDataItem(Integer.parseInt(tbWeight.getText()), date, 
                                        lbCategoryOfMeasure.getSelectedIndex(), 2));
                }
                if(tbBloodPresureDiastolic.getValue()!=""){
                        medicalData.add(new MedicalDataItem(Integer.parseInt(tbBloodPresureDiastolic.getText()), date, 
                                        lbCategoryOfMeasure.getSelectedIndex(), 3));
                }
                if(tbBloodPresureSystolic.getValue()!=""){
                        medicalData.add(new MedicalDataItem(Integer.parseInt(tbBloodPresureSystolic.getText()), date, 
                                        lbCategoryOfMeasure.getSelectedIndex(), 4));
                }
                if(tbPulseRate.getValue()!=""){
                        medicalData.add(new MedicalDataItem(Integer.parseInt(tbPulseRate.getText()), date, 
                                        lbCategoryOfMeasure.getSelectedIndex(), 5));
                }
        }

        /**
         * Pop up to confirm adding of a row in a cell table.
         * @param medicalRecord
         * List of the new medical data added to the data base
         */
        private void askingAdd(final List<MedicalDataItem> medicalRecord){
                String title = "Adding a record";
                String[] messages = { "You are about to add a record, are you sure?" };

                final ConfirmDialogBox box = new ConfirmDialogBox(title, messages);

                box.show();
                box.center();
                box.addCloseHandler(new CloseHandler<PopupPanel>() {
                        @Override
                        public void onClose(CloseEvent<PopupPanel> event) {
                                if(box.getChoice()==ConfirmDialogBox.YES){
                                        try
                                        {
                                                // Add Array to the database.
                                                medicalDataService.sendPOST(Patient.JSON_URL_MEDICALDATA,createJson(medicalRecord));

                                                // Clear text boxes.
                                                resetFields();

                                                // Get new dataSet and refresh table.
                                                manageDataWidget.getData();

                                                // Set statistics as not baked yet because of the new data.
                                                StatisticsWidget.statistics.setDataProcessed(false);
                                                StatisticsWidget.statistics.setStatisticsBaked(false);
                                        }
                                        catch(Exception e)
                                        {
                                                e.printStackTrace();
                                        }
                                }
                        }
                });
        }


        // ----------------------------------------------------------------------------
        // Data methods.
        // ----------------------------------------------------------------------------

        /**
         * Method to generate a medical Data JSON Array
         * @param medicalRecord
         * List of the medical data how will be added
         * @return
         * JSON version of the List of medical data
         */
        private JSONArray createJson(List<MedicalDataItem> medicalRecord){
                // Generate JSONArray
                JSONArray medicalDataArray = new JSONArray();
                JSONObject medicalData;
                int pos=0;

                //fill MedicalDataArray with given Items
                for (MedicalDataItem medicalDataItem : medicalRecord) {
                        //create new medicalDataItem
                        medicalData = new JSONObject();
                        //fill item with values
                        medicalData.put("value", new JSONNumber(medicalDataItem.getValue()));
                        medicalData.put("date", new JSONString(medicalDataItem.getDate()));
                        medicalData.put("category", new JSONNumber(medicalDataItem.getCategory()));
                        medicalData.put("type",new JSONNumber(medicalDataItem.getType()));
                        //add item to the array
                        medicalDataArray.set(pos, medicalData);
                        //increase array position number
                        pos++;
                }
                return medicalDataArray;
        }


        // ----------------------------------------------------------------------------
        // Listeners for the add and reset buttons.
        // ----------------------------------------------------------------------------

        private class AddDataHandler implements ClickHandler {
                @Override
                public void onClick(ClickEvent event) {
                        try{
                                // Total length of all fields values.
                                int dataLength = 0;                     

                                // Check requirement for Category
                                FieldVerifier.validateCategory("Blood sugar",tbBloodSugar,lbCategoryOfMeasure);
                                FieldVerifier.validateCategory("Quantity of insuline",tbQuantityOfInsulin,lbCategoryOfMeasure);

                                dataLength += FieldVerifier.validateAndParseDouble("Blood sugar", tbBloodSugar, 0., 200.);
                                dataLength += FieldVerifier.validateAndParseInt("Quantity of insuline", tbQuantityOfInsulin, 0, 100);
                                dataLength += FieldVerifier.validateAndParseDouble("Weight", tbWeight, 0., 400.);
                                dataLength += FieldVerifier.validateAndParseDouble("Diastolic Blood Presure", tbBloodPresureDiastolic, 0., 300.);
                                dataLength += FieldVerifier.validateAndParseDouble("Systolic Blood Presure", tbBloodPresureSystolic, 0., 300.);
                                dataLength += FieldVerifier.validateAndParseInt("Pulse rate", tbPulseRate, 1, 300);

                                // Check that combined values are > 0 so we know the user entered at least one value.
                                if(dataLength > 0){
                                        // fill item with given information
                                        createMedicalData();
                                        //ask for adding data, if yes add to database
                                        askingAdd(medicalData);
                                        
                                        StatisticsWidget.statistics.setDataProcessed(false);
                                }
                                // In case no value was inserted, inform the user.
                                else{
                                        Window.alert("You must enter at least one value");
                                        return;
                                }
                        }
                        catch (ValidationException e) {
                                Window.alert(e.getMessage());
                        }

                        // Call the refresh method of the manage medical data widget.
                        medicalDataService.sendGetAllMedicalRecords(AddMedicalDataWidget.this);
                }
        }

        // Method click handler for the button Reset data.
        private class ResetDataHandler implements ClickHandler{
                @Override
                public void onClick(ClickEvent event){
                        resetFields();
                }
        }

        @Override
        /**
         * refresh component on the page
         */
        public void refreshComp() {
        }
}